#include "wm_hal.h"

static void _7816_io_clk_config(_7816_HandleTypeDef *h7816, uint8_t mode);
static void _7816_set_freq(_7816_TypeDef *Instance, uint32_t Freq);
static void _7816_set_etu(_7816_TypeDef *Instance, uint32_t FD);

HAL_StatusTypeDef HAL_7816_Init(_7816_HandleTypeDef *h7816)
{
	wm_sys_clk sysclk;
	uint32_t div;
	
	if (h7816 == NULL)
	{
		return HAL_ERROR;
	}
	
	assert_param(IS_7816_INSTANCE(h7816->Instance));
	assert_param(IS_7816_STOPBITS(h7816->Init.StopBits));
	assert_param(IS_7816_LSB_MSB(h7816->Init.LSB_MSB));
	assert_param(IS_7816_T0_1(h7816->Init.T0_1));
	
	MODIFY_REG(h7816->Instance->LC,
             (uint32_t)(_7816_LC_LMSB | _7816_LC_STOPBIT | _7816_LC_T0T1),
             (uint32_t)(h7816->Init.LSB_MSB | h7816->Init.StopBits | _7816_LC_MODE | h7816->Init.T0_1));
		 
	SystemClock_Get(&sysclk);
	div = (sysclk.apbclk * 1000000 + h7816->Init.Freq) / (2 * h7816->Init.Freq) - 1;
	WRITE_REG(h7816->Instance->BAUDR, ((div << _7816_BAUDR_CLKDIV_Pos) | h7816->Init.Fd));
	WRITE_REG(h7816->Instance->WT, h7816->Init.WaitTime);

	return HAL_OK;
}

HAL_StatusTypeDef HAL_7816_DeInit(_7816_HandleTypeDef *h7816)
{
	if (h7816 == NULL)
	{
		return HAL_ERROR;
	}

	assert_param(IS_7816_INSTANCE(h7816->Instance));

	return HAL_OK;
}

void HAL_7816_HotReset(_7816_HandleTypeDef *h7816)
{
	uint32_t delay = 0xFFFF;
	
	// set the reset pin to low
	_7816_RST_L(h7816);
	// delay
	while(delay--);
	// set the F/D to default (372)
	_7816_set_etu(h7816->Instance, h7816->Init.Fd);
	// set the reset pin to high
	_7816_RST_H(h7816);
}

void HAL_7816_ColReset(_7816_HandleTypeDef *h7816)
{
	// set the power pin to low
	_7816_PWR_L(h7816);
	// set the clk and io pin to gpio mode
	_7816_io_clk_config(h7816, 1);
	_7816_CLK_L(h7816);
	_7816_IO_L(h7816);
	// set the reset pin in low level
	_7816_RST_L(h7816);
	// set the power pin to high
	_7816_PWR_H(h7816);
	// enable 7816 mode
	_7816_MODE_EN(h7816->Instance);
	// set the clk and io pin to 7816 mode
	_7816_io_clk_config(h7816, 0);
	// config the clock frequence
	_7816_set_freq(h7816->Instance, h7816->Init.Freq);
	// set the F/D to default (372)
	_7816_set_etu(h7816->Instance, h7816->Init.Fd);
	// set the reset pin to high
	_7816_RST_H(h7816);
}

void HAL_7816_Deactive(_7816_HandleTypeDef *h7816)
{
	// set the reset pin in low level
	_7816_RST_L(h7816);
	// set the clk and io pin to 7816 mode
	_7816_io_clk_config(h7816, 0);
	// disable the output clock
	_7816_CLK_DISABLE(h7816->Instance);
	// set the clk and io pin to gpio mode
	_7816_io_clk_config(h7816, 1);
	_7816_CLK_L(h7816);
	_7816_IO_L(h7816);
	// set the power pin to low
	_7816_PWR_L(h7816);
}

// mode : 0 -- 7816 mode;
//		  1 -- gpio mode;
static void _7816_io_clk_config(_7816_HandleTypeDef *h7816, uint8_t mode)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	if (mode)
	{
		GPIO_InitStruct.Pin = h7816->Init.CLK_Pin;
		GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
		GPIO_InitStruct.Pull = GPIO_PULLUP;
		HAL_GPIO_Init(h7816->Init.CLK_Port, &GPIO_InitStruct);
		GPIO_InitStruct.Pin = h7816->Init.IO_Pin;
		HAL_GPIO_Init(h7816->Init.IO_Port, &GPIO_InitStruct);
	}
	else
	{
		__HAL_AFIO_REMAP_7816_CLK(h7816->Init.CLK_Port, h7816->Init.CLK_Pin);
		__HAL_AFIO_REMAP_7816_IO(h7816->Init.IO_Port, h7816->Init.IO_Pin);
	}
}

static void _7816_set_freq(_7816_TypeDef *Instance, uint32_t Freq)
{
	wm_sys_clk sysclk;
	uint32_t div;
	
	SystemClock_Get(&sysclk);
	div = (sysclk.apbclk * 1000000 + Freq) / (2 * Freq) - 1;
	MODIFY_REG(Instance->BAUDR, _7816_BAUDR_CLKDIV, (div << _7816_BAUDR_CLKDIV_Pos));
}

static void _7816_set_etu(_7816_TypeDef *Instance, uint32_t FD)
{
	MODIFY_REG(Instance->BAUDR, _7816_BAUDR_FD, (FD << _7816_BAUDR_FD_Pos));
}
